package com.pandi.springboot_jwt.config;


import com.pandi.springboot_jwt.component.MyRealm;
import com.pandi.springboot_jwt.intercepter.MyAuthenticationFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * @author pandi
 * @create 2021-01-05 14:54
 * 整合shiro框架的相关配置
 */

@Configuration
public class ShiroConfig {


    /**
     * 1.创建shiroFilter
     *   负责拦截所有请求
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){


        /**
         * 在这里提一嘴，因为shiro这个框架非常的老，老到和前后端不分离时jsp技术集成度非常高
         * 我限制了/test资源为受限资源，我去postman请求这个资源给钱我返回的时404："path": "/login.jsp"
         * 侧面说明shiro内部有写默认的请求不通过跳转页面也就是login.jsp
         */

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //注意过滤器配置顺序 不能颠倒
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了，登出后跳转配置的loginUrl
        filterChainDefinitionMap.put("/logout", "logout");
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/login3", "anon");
        filterChainDefinitionMap.put("/save_staff", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        //配置shiro默认登录界面地址，前后端分离中登录界面跳转应由前端路由控制，后台仅返回json数据
//        shiroFilterFactoryBean.setLoginUrl("/unauth");
        // 登录成功后要跳转的链接
//        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权界面;
//        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //配置统一未登录返回信息
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("authc", new MyAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }


    /**
     * 2.创建安全管理器
     * 在web环境下创建的是DefaultWebSecurityManager安全管理器。
     *
     */
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("realm") Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        defaultWebSecurityManager.setSessionManager(sessionManager());
        return defaultWebSecurityManager;
    }


    /**
     * 3.创建自定义realm
     *
     */
    @Bean(name = "realm")
    public Realm getRealm(){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());

        /**
         * 开启shiro官方缓存管理器
         *  这个缓存管理器固然不错，但是缺点也是比较明显的（这个缓存是项目的本地缓存）,如果项目意外宕机,重启后还是会第一次去查询数据库
         *  项目较小,用户少，无高并发，官方默认缓存管理器是绝对够用的
         *  反之，如果是个大型项目 用户数大，抱着能省则省的策略还是用redis来实现分布式缓存最为靠谱
         * @return
         */
//        myRealm.setCacheManager(new EhCacheManager());
        myRealm.setCachingEnabled(true);   //开启全局缓存
        myRealm.setAuthenticationCachingEnabled(true); //开启认证缓存
        myRealm.setAuthenticationCacheName("authentication");//认证缓存起别名
        myRealm.setAuthorizationCachingEnabled(true);  //开启授权缓存
        myRealm.setAuthorizationCacheName("authorization"); //授权缓存起别名

        myRealm.setCacheManager(new RedisCacheManager());  //开启Redis缓存管理

        return myRealm;
    }

    /**
     *
     * 开启shiro的凭证匹配器 校验数据库查询到的加密名密码和明文密码的比对
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数，比如散列两次，相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }


    /**
     * 自定义的 shiro session 缓存管理器，用于跨域等情况下使用 token 进行验证，不依赖于sessionId
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        //将我们继承后重写的shiro session 注册
        ShiroSessionConfig shiroSession = new ShiroSessionConfig();
        //如果后续考虑多tomcat部署应用，可以使用shiro-redis开源插件来做session 的控制，或者nginx 的负载均衡
        shiroSession.setSessionDAO(new EnterpriseCacheSessionDAO());
        return shiroSession;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


    /**
     *
     * 实现redis分布式缓存策略
     */







}
